<?php
/*
Auth:Sang
Desc:应用服务器类
Date:2014-11-01
*/
namespace Lib;

class AppServer extends HttpServer{
    public $worker;
    private $auto_load = array('cache','session','db','storage','mongodb');
    public function __construct($config){
        parent::__construct($config);
        Loader::setRootNS('App',DOCUMENT_ROOT);
        Loader::setRootNS('Act',APP_PATH.'Act/');
        Loader::setRootNS('Widget',WIDGET_PATH);
        Loader::setRootNS('Task',DOCUMENT_ROOT.'/Task/');
        $_SERVER['DOCUMENT_ROOT'] = DOCUMENT_ROOT;
        if(!empty($this->c('app.charset'))){
            $this->charset = $this->c('app.charset');
        }
        \Lib\Error::$echo_html = true;
        set_error_handler(array($this,'errorHandler'));
    }

    public function __get($name){
        if(empty($this->$name) && in_array($name,$this->auto_load)){
            $class = '\\Lib\\'.ucwords($name);
            $this->$name = new $class(isset($this->config[$name]) ? $this->config[$name] : array());
        }
        return $this->$name;
    }

    public function __set($name,$val){
        $this->$name = $val;
    }
    public function __isset($name){
        return isset($this->$name);
    }

	public function start($serv){
        $this->worker = $serv;
        $path_info = $_SERVER['PATH_INFO'];
        $request_methods = array('get'=>'doGet','post'=>'doPost','add'=>'doAdd','update'=>'doUpdate','put'=>'doUpdate','delete'=>'doDelete');
        $method = 'get';
        if( $path_info=='/'){
            $path_info .= 'index';
        }elseif(substr($path_info,-1)=='/'){
            $path_info .= 'get';
        }
        $path_info = trim($path_info,'/');
        $reqs = explode('/',$path_info);
        if(!empty($path_info) && count($reqs)==1){
            $act = $reqs[0];
            if(is_post()){
                $method = 'post';
            }elseif(is_put()){
                $method = 'put';
            }elseif(is_delete()){
                $method = 'delete';
            }
        }elseif(count($reqs)>1){
            $m = array_pop($reqs);
            if(in_array($m,array_keys($request_methods))){
                $method = $m;
            }else{
                $reqs[] = $m;
                if(is_post()){
                    $method = 'post';
                }elseif(is_put()){
                    $method = 'put';
                }elseif(is_delete()){
                    $method = 'delete';
                }
            }
            array_walk($reqs,function(&$v,$k){$v = ucwords($v);});
            $act = join('\\',$reqs);
        }else{
            $act = 'index';
        }

        $act = ucwords($act);
        $act_class = '\\Act\\'.$act;
        $act_file = APP_PATH.'Act/'.str_replace('\\','/',$act).'.php';
        if(!in_array($method,array_keys($request_methods)) || !is_file($act_file) || !class_exists($act_class)){
        	return $this->http404();
        }
        try{
            apply_action('app_start',$act_file);
            $this->session->start();
            ob_start();
            $act_obj = new $act_class($this,$method);
            $data = $act_obj->$request_methods[$method]();
            if($data && !is_string($data)){
                $data = json_encode($data);
            }
            $data .= ob_get_contents();
            ob_end_clean();
            apply_action('on_response',$data);
            $this->response($this->serv,$this->currentFd,$data);
            apply_action('app_end',$act,$method);
        }catch(\Exception $e){
            $response = $e->getMessage();
            // 当错误代码为0时，表示是人为抛出错误，此时模仿PHP原生EXIT函数，退出并输出信息
            $code = $e->getCode();
            if($code==0){
                $this->response($this->serv,$this->currentFd,$response);
                return;
            }elseif(defined('DEBUG')){
                $info = 'Code:'.$code.' '.$e->getMessage();
                if($e->getCode()>1){
                    $file = explode('.',$e->getFile())[0];
                    $info .= '<br>in '.str_replace([SHM_PATH,APP_PATH,FRAME_ROOT],'',$file).' : '.$e->getLine();
                }
                $error = \Lib\Error::info('Runtime error:',$info,$code>1?$code:999);
            }else{
                $error = \Lib\Error::info('','页面发生错误<hr>请联系：190276134@qq.com',999);
            }
            $this->response($this->serv,$this->currentFd,$error,500);
        }finally{
            //自动保存session
            $this->session->save();
            // 删除临时文件，有一定的概率
            if(rand(1,9)==5){
                exec('rm -f /tmp/sw_tmp_*');
            }
            //当内存占用率过大时，自动退出重启
            $memory_used = memory_get_peak_usage(true);
            if($memory_used/1024/1024 > 200){
                exit;
            }
        }
	}

    public function errorHandler($errno, $errstr, $errfile, $errline){
        $errfile = explode('.',$errfile)[0];
        $errorMsg = "{$errstr} (".str_replace(array(SHM_PATH.'tpls/',SHM_PATH.'widget/tpl/',APP_PATH,FRAME_ROOT,DOCUMENT_ROOT),array('tpl:','widget_tpl:','app:','root:','app:'),$errfile).":{$errline})";
        if($this->currentFd){
            $this->response($this->serv,$this->currentFd,\Lib\Error::info("Runtime Error", $errorMsg),500);
        }else{
            print_r($errorMsg.PHP_EOL);
        }
    }
}